#include "Parser.h"
#include <direct.h>

extern int extentCounter;

Parser::Parser(void)
{
}

Parser::Parser(string configFile, string userFile){
	dataFileNames =  new vector<string>();
	confFileName = configFile;
	userFileName = userFile;
	view = 0;
	SSD_num = 0;
	FC_num = 0;
	SATA_num = 0;
	response_times = 0;
	ourTime = 0;
	dataFileIndex = 0;
	whichAlgo=0;
	check_cost = 0;
	max_time_slices = -1;
}

Parser::~Parser(void)
{
	delete dataFileNames;
	delete response_times;
	if (view) {
		delete view;
		view = 0;
	}
}

// clear system before starting a new simulation
void Parser::startOver()
{
	dataFileIndex = 0;
	ourTime = 0;
	view->startOver();
}

// create everything needed for a new system
SystemView* Parser::createSystem()
{
	response_times= new vector<double>();
	view = new SystemView();
	ParseStorageCharacteristics(confFileName);
	ParseUserInput(userFileName);
	view->divide_system(SSD_num,FC_num,SATA_num);
	view->getAllocation()->setResponseTimes(response_times);
	return view;
}

void Parser::ParseUserInput(string FileName)
{
	ifstream input;
	int index, i, j, count, whoo, int_to, int_from, num_of_luns;
	char data[200];
	char split[200];
	char who[10], from[10], to[10];
	Cache* new_cache = (Cache*)malloc(sizeof(Cache));
	DeviceData* device;
	double gp1, gp2, gp3;

	input.open(FileName);
	if (!input.is_open()){
		cout << "Error opening file "<<FileName << endl;
		exit(2);
		return;
	}

	while (!input.eof()){
		input >> data;
		// reading number of partitions
		if (((string)data).find("nof_partitions") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			new_cache->nof_partitions = atoi(split);
			new_cache->partition_sizes = new int[new_cache->nof_partitions];
			new_cache->partition_luns = new int*[new_cache->nof_partitions];
			((string)data).clear();
			((string)split).clear();
		}
		// reading size of a parition
		else if ((((string)data).find("part") != string::npos) && (((string)data).find("size") != string::npos)){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			index = ((string)data).find_first_of("_")+1;
			((string)data).copy(who, ((string)data).find_first_of("_", index) - (index), index);
			who[((string)data).find_first_of("_", index) - (index)] = '\0';
			new_cache->partition_sizes[atoi(who)-1] = atoi(split);
			((string)data).clear();
			((string)split).clear();
			((string)who).clear();
		}
		// reading which luns belong to the partition
		else if ((((string)data).find("part") != string::npos) && (((string)data).find("luns") != string::npos)){
			index = ((string)data).find_first_of("_")+1;
			((string)data).copy(who, ((string)data).find_first_of("_", index) - (index), index);
			who[((string)data).find_first_of("_", index) - (index)] = '\0';
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			whoo = atoi(who) - 1;

			index = 0;
			count = 0;
			// count amount of luns in this parition
			while (index < ((string)split).length()){
				i = ((string)split).find_first_of("-", index);
				j = ((string)split).find_first_of(",", index);
				// a range of numbers
				if ((i != string::npos) && ((j == string::npos) || (i < j))) {
					((string)split).copy(from, i - index, index);
					from[i-index] = '\0';
					// last number
					if (j == string::npos){
						((string)split).copy(to, ((string)split).length() - (i+1), (i+1));
						to[((string)split).length() - index] = '\0';
						index = ((string)split).length();
					}
					else {
						((string)split).copy(to, j - (i+1), (i+1));
						to[j - (i+1)] = '\0';
						index = j+1;
					}
					stringstream convert (to);
					convert >> std::hex >> int_to;
					stringstream convert2 (from);
					convert2 >> std::hex >> int_from;
					count += (int_to - int_from) + 1; 
					((string)from).clear();
					((string)to).clear();
				}
				// one number
				else if (i == string::npos) {
					count++;
					// last number
					if (j == string::npos){
						index = ((string)split).length();
					}
					else {
						index = j+1;
					}
				}
			}
			new_cache->partition_luns[whoo] = new int[count];
			index = 0;
			count = 0;
			// add lun IDs to the array of this partition
			while (index != ((string)split).length()){
				i = ((string)split).find_first_of("-", index);
				j = ((string)split).find_first_of(",", index);
				// a range of numbers
				if (((j == string::npos) && (i != string::npos)) || (i != string::npos) && (i < j)) {
					((string)split).copy(from, i - index, index);
					from[i - index] = '\0';
					// last number
					if (j == string::npos){
						((string)split).copy(to, ((string)split).length() - (i+1), i+1);
						to[((string)split).length() - (i+1)] = '\0';
						index = ((string)split).length();
					}
					else {
						((string)split).copy(to, j - (i+1), (i+1));
						to[j - (i+1)] = '\0';
						index = j+1;
					}
					stringstream convert (to);
					convert >> std::hex >> int_to;
					stringstream convert2 (from);
					convert2 >> std::hex >> int_from;
					for (i = int_from; i <= int_to; i++, count++){
						new_cache->partition_luns[whoo][count] = i;
					}
					((string)to).clear();
				}
				// one number
				else if (i == string::npos) {

					// last number
					if (j == string::npos){
						((string)split).copy(from, ((string)split).length() - index, index);
						from[((string)split).length() - index] = '\0';
						index = ((string)split).length();
					}
					else {
						((string)split).copy(from, j - index, index);
						from[j - index] = '\0';
						index = j+1;
					}
					stringstream convert2 (from);
					convert2 >> std::hex >> int_from;
					new_cache->partition_luns[whoo][count] = int_from;
					count++;
				}
				((string)from).clear();
			}

			((string)data).clear();
			((string)split).clear();
			((string)who).clear();
		}
		else if (((string)data).find("util_SSD") != string::npos){
			device = view->getDevice("SSD");
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			device->setMaxUtil(atof(split));
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("util_FC") != string::npos){
			device = view->getDevice("FC");
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			device->setMaxUtil(atof(split));
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("util_SATA") != string::npos){
			device = view->getDevice("SATA");
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			device->setMaxUtil(atof(split));
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("SSD_num") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			SSD_num = atoi(split);
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("FC_num") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			FC_num = atoi(split);
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("SATA_num") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			SATA_num = atoi(split);
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("DRT1") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			response_times->push_back( atof(split));
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("DRT2") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			response_times->push_back( atof(split));
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("DRT3") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			response_times->push_back( atof(split));
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("whichAlgo") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			whichAlgo= atoi(split);
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("group_percents1") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			gp1= atof(split);
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("group_percents2") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			gp2= atof(split);
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("group_percents3") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			gp3= atof(split);
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("num_of_luns") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			num_of_luns= atoi(split);
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("data_file=") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			dataFileNames->push_back(split);
			((string)data).clear();
			((string)split).clear();
		}	
		else if (((string)data).find("system_cost=") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			view->getAllocation()->setSystem_cost(atof(split));
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("fix_sata=") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			view->getAllocation()->setFix_sata(atof(split));
			((string)data).clear();
			((string)split).clear();
		}
		else if (((string)data).find("exchange_limit=") != string::npos){
			index = ((string)data).find("=")+1;
			((string)data).copy(split, ((string)data).length() - index, index);
			split[((string)data).length() - index] = '\0';
			view->setLimit(atoi(split));
			((string)data).clear();
			((string)split).clear(); 
		}
	}
	input.close();
	view->getAllocation()->setGroupsPercentsNumOfLuns(gp1,gp2,gp3,num_of_luns);
	// calculate cache size
	new_cache->cache_size = 0;
	for (int i = 0; i < new_cache->nof_partitions; i++) {
		new_cache->cache_size += new_cache->partition_sizes[i];
	}

	view->setMyCache(new_cache);

	return;
}


void Parser::ParseStorageCharacteristics(string filename){
	ifstream myData;

	myData.open(filename);
	char input[101];
	char Type[101];
	double Data[6]; //Capacity, Cost, Overhead, R_rate, W_rate, Energy
	if (myData.is_open()) {
		while (!myData.eof()) {
		
			myData >> input;

			if (((string)input).find_first_of('#')==string::npos) {

				for (int i = 0; i < 7; i++) {
					if (i==0) {
						int found = ((string)input).find_first_of(':');
						Type[found] = 0;
						((string)input).copy(Type, found, 0);
					}
					else {
						Data[(i-1)] = atof(input);
					}
					if (i != 6) {
						myData >> input;
					}
				}
				
				DeviceData *d = new DeviceData(Type, (int)Data[0], (int)Data[1], Data[2], (int)Data[3], (int)Data[4], (int)Data[5]);

				view->addDevice(d);
			}
		}
	}
	
	myData.close();
}

vector<ExtentEntry*>* Parser::ParseNextTimeSlice(){
	if (!dataFile.is_open()){ 	// first file open
		char buff[10000];
		_getcwd( buff, 10000 );
		cout<<buff<<endl;
		dataFile.open(dataFileNames->at(dataFileIndex));
		if (dataFile.is_open())
			cout<<"file really open"<<endl;
		cout <<"Open file ==> "<< dataFileNames->at(dataFileIndex)<< endl;
	}

	if(dataFile.eof()){ // end of file=> check if there are more files 
		dataFile.close();
		dataFileIndex++;
		if(dataFileIndex == dataFileNames->size())
			return 0;
		dataFile.open(dataFileNames->at(dataFileIndex));
		cout <<"Open file ==> "<< dataFileNames->at(dataFileIndex)<< endl;
	}

	ExtentStatistics* stats;
	Extent* e;
	ExtentEntry* entry;
	vector<ExtentEntry*>* ans = new  vector<ExtentEntry*>();
	
	int  i ,entry_time , lun_id , currTime = -1;
	__int64 offset;
	
	if (tokens.size()==17)
		goto L;
	
	while (dataFile.getline(line,200) && (!check_cost || (ourTime < max_time_slices))) {
	
		tokens.clear();
		
		char * pch;
		pch = strtok (line," ");
		i=0;
		while (pch != NULL)
		{
			tokens.push_back(pch);
			pch = strtok (NULL, " ");
			i++;
		}

		if(tokens.size() != 17)
			continue;
		L:
		entry_time = atoi(tokens[0]);

		if(entry_time == 0 || entry_time%3600 != 0)//check valid format
			continue;
						
		// first new time slice entry
		if(currTime == -1){ 
			currTime = entry_time;
			ourTime++;
			view->increaseTimeSlices();
		}
		
		//last new time slice entry
		if(currTime != entry_time)
			break;
		
		stats = new ExtentStatistics(ourTime*3600);
		stringstream convert (tokens[3]);
        convert >> std::hex >> lun_id;
		offset = _atoi64(tokens[4]);
		for(i = 6; i<17 ; i++)
			stats->statistics[i-6] = atoi(tokens[i]);
		e = view->getExtent(lun_id, offset); 
		entry = new ExtentEntry (e, ourTime*3600, stats); 
		view->updateLuns(lun_id,entry);/* default priority is 1*/
		ans->push_back(entry);
	}
	cout<<"extent count: "<< extentCounter<<endl;
	return ans;
}  

// set paramtets for system recommendation
void Parser::setSystemRecommendation(int check, int max_ts){
	check_cost = check;
	max_time_slices = max_ts;
}